<?php

final class Connector
{
    private $joinData;
    private $joinValue;
    private $whereData;
    private $whereValue;
    private $groupData;
    private $groupValue;
    private $orderData;
    private $limitData;
    private $initGroup;
    private $initJoin;

    //单例化
    static public function share()
    {
        static $instance = null;
        if (is_null($instance) || isset($instance)) {
            $instance = new Connector();
            $instance->reset();
        }
        return $instance;
    }

    private function __construct()
    {
    }

    public function __clone()
    {
    }

    private function connect()
    {
        static $instance = null;
        if (is_null($instance)) {
            $adh = App::$datahost;
            $adb = App::$database;
            $dns = "mysql:host={$adh};dbname={$adb};charset=UTF8";
            try {
                $pdo = new PDO($dns, App::$username, App::$password, array(PDO::ATTR_PERSISTENT => true));
                $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
            } catch (PDOException $e) {
                die('Connection failed: ' . $e->getMessage());
            }
            $instance = $pdo;
            $this->reset();
        }
        return $instance;
    }

    private function field($table)
    {
        static $tmpField = [];
        if (empty($tmpField[$table])) {
            $adb = App::$database;
            $sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=\"{$table}\" AND TABLE_SCHEMA=\"{$adb}\"";
            $dat = $this->query($sql)->fetchAll(PDO::FETCH_ASSOC);
            //
            $tmp = [];
            foreach ($dat as $key => $value) {
                $tmp[] = $value['COLUMN_NAME'];
            }
            //
            $tmpField[$table] = $tmp;
        } else {
            $tmp = $tmpField[$table];
        }
        return $tmp;
    }

    private function format($table, $data)
    {
        $source = [];
        $column = $this->field($table);
        foreach ($data as $k => $v) {
            if (in_array($k, $column)) {
                $source[$k] = $v;
            }
        }
        return $source;
    }

    public function reset()
    {
        $this->joinData = "";
        $this->joinValue = [];
        $this->whereData = "";
        $this->whereValue = [];
        $this->groupData = "";
        $this->groupValue = [];
        $this->orderData = "";
        $this->limitData = "";
        $this->initGroup = false;
        $this->initJoin = false;
        return $this;
    }

    public function query($sql, $data = null)
    {
        $stm = $this->connect()->prepare($sql);
        $stm->execute($data);
        return $stm;
    }

    public function delete($table)
    {
        if (empty($this->whereData) == false) {
            $sql = "DELETE FROM {$table} {$this->whereData}";
            $stm = $this->query($sql, $this->whereValue);
            $this->reset();
            //
            return $stm->rowCount();
        }
        return 0;
    }

    public function update($table, $data)
    {
        if (empty($this->whereData) == false) {
            $source = $this->format($table, $data);
            if (empty($source) == false) {
                $key = [];
                $val = [];
                foreach ($source as $k => $v) {
                    $key[] = "{$k} = ?";
                    $val[] = $v;
                }
                //
                $dat = array_merge($val, $this->whereValue);
                $sql = "UPDATE {$table} SET " . implode(",", $key) . " {$this->whereData}";
                $stm = $this->query($sql, $dat);
                $this->reset();
                //
                return $stm->rowCount();
            }
        }
        return 0;
    }

    public function insert($table, $data)
    {
        $source = $this->format($table, $data);
        if (empty($source) == false) {
            $tmp = [];
            $key = [];
            $val = [];
            foreach ($source as $k => $v) {
                $tmp[] = "?";
                $key[] = $k;
                $val[] = $v;
            }
            //
            $sql = "INSERT INTO {$table} (" . implode(",", $key) . ") VALUES (" . implode(",", $tmp) . ")";
            $stm = $this->query($sql, $val);
            $this->reset();
            //
            return $stm->rowCount();
        }
        return 0;
    }

    public function getId()
    {
        return $this->connect()->lastInsertId();
    }

    public function select($table, $field = "*")
    {
        $dat = array_merge($this->joinValue, $this->whereValue, $this->groupValue);
        $sql = "SELECT {$field} FROM {$table} {$this->joinData} {$this->whereData} {$this->groupData} {$this->orderData} {$this->limitData}";
        $stm = $this->query($sql, $dat);
        $this->reset();
        //
        return $stm;
    }

    public function where($key, $value, $operator = "=", $join = "AND")
    {
        if (empty($this->whereData)) {
            $this->whereData = "WHERE {$key} {$operator} ?";
        } else {
            $this->whereData .= " {$join} {$key} {$operator} ?";
        }
        $this->whereValue[] = $value;
        return $this;
    }

    public function join($table, $prefix = "INNER")
    {
        if (empty($this->joinData)) {
            $this->joinData = "{$prefix} JOIN {$table}";
        } else {
            $this->joinData .= " {$prefix} JOIN {$table}";
        }
        $this->initJoin = true;
        return $this;
    }

    public function on($key, $value, $operator = "=", $join = "AND")
    {
        if (empty($this->joinData) == false) {
            if ($this->initJoin == true) {
                $this->initJoin = false;
                $join = "ON";
            }
            $this->joinData .= " {$join} {$key} {$operator} ?";
            $this->joinValue[] = $value;
        }
        return $this;
    }

    public function group($key)
    {
        if (empty($this->groupData)) {
            $this->groupData = "GROUP BY {$key}";
        } else {
            $this->groupData .= " ,{$key}";
        }
        $this->initGroup = true;
        return $this;
    }

    public function having($key, $value, $operator = "=", $join = "AND")
    {
        if (empty($this->groupData) == false) {
            if ($this->initGroup == true) {
                $this->initGroup = false;
                $join = "HAVING";
            }
            $this->groupData .= " {$join} {$key} {$operator} ?";
            $this->groupValue[] = $value;
        }
        return $this;
    }

    public function order($key, $sort = "ASC")
    {
        if (empty($this->orderData)) {
            $this->orderData = "ORDER BY {$key} {$sort}";
        } else {
            $this->orderData .= " , {$key} {$sort}";
        }
        return $this;
    }

    public function limit($page, $size = -1)
    {
        $this->limitData = "LIMIT {$page} , {$size}";
        return $this;
    }
}